package org.fusionide.util;

import java.util.regex.*;

public class RegEx {
	public static final String newline = System.getProperty("line.separator");

	private Pattern pattern;

	private Matcher matcher;

	private int matchCount;

	private int groupCount;

	private int currentMatch = 0;

	private boolean matched = false;

	public RegEx() {
	}

	public RegEx(String pattern, String flags, String input) {
		this.compile(pattern, flags);
		this.match(input);
	}

	public RegEx(String pattern, String flags) {
		this.compile(pattern, flags);
	}

	public RegEx(String pattern) {
		this.compile(pattern);
	}

	public static RegEx Compile(String pattern, String flags, String input) {
		return new RegEx(pattern, flags, input);
	}

	public static RegEx Compile(String pattern, String flags) {
		return new RegEx(pattern, flags);
	}

	public static RegEx Compile(String pattern) {
		return new RegEx(pattern);
	}

	private int getFlags(String flags) {
		int regexflags = 0;
		RegEx rx = new RegEx();

		if (flags == null)
			return regexflags;
		rx.compileNoCase("((?:\\bCANON_EQ|CASE_INSENSITIVE|COMMENTS|DOTALL|MULTILINE|UNICODE_CASE|UNIX_LINES\\b)|[CIXSMUD])", flags);

		if (rx.isMatched()) {
			while (rx.next()) {
				for (int j = 1; j <= rx.getGroupCount(); j++) {
					String option = rx.group(j);
					if (option.equalsIgnoreCase("CANON_EQ") || option.equalsIgnoreCase("C"))
						regexflags |= Pattern.CANON_EQ;
					else if (option.equalsIgnoreCase("CASE_INSENSITIVE") || option.equalsIgnoreCase("I"))
						regexflags |= Pattern.CASE_INSENSITIVE;
					else if (option.equalsIgnoreCase("COMMENTS") || option.equalsIgnoreCase("X"))
						regexflags |= Pattern.COMMENTS;
					else if (option.equalsIgnoreCase("DOTALL") || option.equalsIgnoreCase("S"))
						regexflags |= Pattern.DOTALL;
					else if (option.equalsIgnoreCase("MULTILINE") || option.equalsIgnoreCase("M"))
						regexflags |= Pattern.MULTILINE;
					else if (option.equalsIgnoreCase("UNICODE_CASE") || option.equalsIgnoreCase("U"))
						regexflags |= Pattern.UNICODE_CASE;
					else if (option.equalsIgnoreCase("UNIX_LINES") || option.equalsIgnoreCase("D"))
						regexflags |= Pattern.UNIX_LINES;
				}
			}
		}
		return regexflags;
	}

	public void compile(String pattern) {
		this.pattern = Pattern.compile(pattern);
	}

	public void compile(String pattern, String flags) {
		this.pattern = Pattern.compile(pattern, getFlags(flags));
	}

	private void compileNoCase(String pattern, String input) {
		this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
		this.match(input);
	}

	public RegEx match(Object o) {
		return match(o.toString());
	}

	public RegEx match(String input) {
		if (input == null) {
			matched = false;
			matchCount = -1;
			groupCount = -1;
		} else {
			matcher = pattern.matcher(input);
			if (!next()) {
				// matcher = null;
				matched = false;
				matchCount = -1;
				groupCount = -1;
			} else {
				int count = 1;
				groupCount = matcher.groupCount();
				while (next())
					count++;
				matchCount = count;
				matcher.reset();
				matched = true;
			}
		}
		return this;
	}

	public void reset() {
		currentMatch = 0;
		matcher.reset();
	}

	public boolean next() {
		if (matcher != null) {
			currentMatch++;
			return matcher.find();
		} else
			return false;
	}

	public RegEx getNext() {
		next();
		return this;
	}

	public int getGroupCount() {
		return groupCount;
	}

	public int getMatchCount() {
		return matchCount;
	}

	public String group() {
		return matcher.group();
	}

	public String group(int index) {
		if (isMatched())
			return matcher.group(index);
		else
			return null;
	}

	public int end() {
		return matcher.end();
	}

	public int end(int group) {
		return matcher.end(group);
	}

	public static String getFlags() {
		String out = "";
		out += "CANON_EQ - C" + newline;
		out += "CASE_INSENSITIVE - I" + newline;
		out += "COMMENTS - X" + newline;
		out += "DOTALL - S" + newline;
		out += "MULTILINE - M" + newline;
		out += "UNICODE_CASE - U" + newline;
		out += "UNIX_LINES - D" + newline;
		return out;
	}

	public boolean isMatched() {
		return matched;
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		int currentMatchContext = currentMatch;
		reset();

		sb.append("Matches: " + matchCount);
		sb.append(newline);
		sb.append("Groups: " + groupCount);
		sb.append(newline);
		sb.append(newline);
		while (next()) {
			sb.append("Match (" + currentMatch + "):" + group(0));
			sb.append(newline);
			for (int i = 1; i <= groupCount; i++) {
				sb.append("\tGroup (" + i + "):" + group(i));
				sb.append(newline);
			}
			sb.append(newline);
		}
		reset();

		while (currentMatch < currentMatchContext) {
			next();
		}
		return sb.toString();
	}
}
